/**
 * 处理运算字符串，计算出运算结果
 * @author janson <janson8908@126.com>
 * @description 运算过程中使用了中缀表达式转后缀表达式（逆波兰表达式），经过测试，对于tan[cot]((2n + 1)π / 2)计算进，会出现错误，该问题目前尚未处理。
 */
var Calculator = (function() {
    function Calculator() {
        this.numRegx = /\d+\.?\d*/;
        this.operatorRegx = /\+|\-|\*|×|\/|÷|%|\^/;
        this.mathRegx = /sin|cos|tan|cot|abs/;
        this.absRegx = /\|\(?(\(?\-?\d+[\.\+\-\*\/\^]?\)?)*\-?\d+\)?\|/g;
        this.charList = [];
        this.result = [];
        // 判断正负
        this.isChar = false;
        // 正负号
        this.plusOrMinus = "+";
        // 为了防止出现0.1+0.2≠0.3，添加一个修改数
        this.fixNum = 1000000000;
        this.initOperator();
    }

    Object.assign(Calculator.prototype, {
        // 初始化运算函数
        initOperator() {
            let operatorObj = {
                "+": this.sum,
                "-": this.minus,
                "*": this.mul,
                "×": this.mul,
                "/": this.div,
                "÷": this.div,
                "%": this.mod,
                "^": this.pow,
            }
            this.operators = operatorObj;
        },
        // 通过传入的运算符，获取运算函数
        getOperator(operat) {
            if(operat=="cot") {
                let cotFunc = function(num) {
                    return 1 / Math.tan(num);
                }
                return cotFunc;
            }
            if (operat.search(this.mathRegx) == 0) {
                return Math[operat];
            }
            return this.operators[operat];
        },

        // 处理输入的字符串，得到有效的项
        getCharactor(str) {
            let charactor;
            while(str) {
                if (str.indexOf("(") == 0) {
                    charactor = "(";
                    if (!this.isChar){
                        str = "*" + str;
                        continue;
                    }
                    str = str.substring(1);
                    this.charList.push(charactor);
                    this.isChar = true;
                    continue;
                }
                if (str.indexOf(")") == 0) {
                    charactor = ")";
                    str = str.substring(1);
                    this.rollBackChar("(");
                    this.isChar = false;
                    continue;
                }
                if (str.search(this.numRegx) == 0) {
                    if (this.plusOrMinus == "-") {
                        this.result.push("-1");
                        str = "*" + str;
                        this.isChar = false;
                        this.plusOrMinus = "+";
                        continue;
                    }
                    charactor = str.match(this.numRegx)[0];
                    let len = charactor.length;
                    str = str.substring(len);
                    this.result.push(charactor);
                    this.isChar = false;
                    continue;
                }
                if (str.search(this.operatorRegx)==0) {
                    charactor = str.match(this.operatorRegx)[0];
                    let len = charactor.length;
                    str = str.substring(len);
                    // result.push(charactor);
                    this.resolveChar(charactor);
                    this.isChar = true;
                    continue;
                }
                if (str.search(this.mathRegx) == 0) {
                    charactor = str.match(this.mathRegx)[0];
                    if(!this.isChar && this.charList.length > 0) {
                        str = "*" + str;
                        continue;
                    }
                    let len = charactor.length;
                    str = str.substring(len);
                    // result.push(charactor);
                    this.resolveChar(charactor);
                    this.isChar = true;
                    continue;
                }
                throw new Error("表达式错误")
            }
        },

        // 回滚符号
        rollBackChar(char) {
            let item = this.charList.pop();
            if (char) {
                while(item != char) {
                    this.result.push(item);
                    item = this.charList.pop();
                }
            } else {
                while(item) {
                    this.result.push(item);
                    item = this.charList.pop();
                }
            }
        },

        // 处理符号
        resolveChar(char) {
            if (this.charList.length == 0) {
                this.charList.push(char);
                return;
            }
            if (this.isChar && char.search(this.mathRegx) == -1) {
                if (char != "+" && char != "-") {
                    throw new Error("表达式错误")
                }
                if (this.plusOrMinus == char) {
                    this.plusOrMinus = "+"
                } else {
                    this.plusOrMinus = "-";
                }
                return;
            }
            let item = this.charList[this.charList.length - 1];
            while(this.charList.length > 0 && this.getWeight(char) <= this.getWeight(item)) {
                this.result.push(this.charList.pop());
                item = this.charList[this.charList.length - 1];
            }
            this.charList.push(char);
        },

        // 获取操作符的优先级
        getWeight(char) {
            if (char == "(") {
                return 0;
            }
            if (char == "+" || char == "-") {
                return 1;
            }
            if (char == "^") {
                return 4;
            }
            if (char.search(this.mathRegx) == 0) {
                return 3;
            }
            return 2;
        },

        // 计算后缀表达式
        getSuffix(str) {
            str = str.replace(/\s/g, "");
            str = str.replace(/\d*π\d*/g, function(str) {
                if (str.length == 1) {
                    return Math.PI;
                }
                if (str.indexOf("π") == 0) {
                    return str.replace("π", Math.PI + "*");
                }
                if (str.indexOf("π")==str.length - 1) {
                    return str.replace("π", "*" + Math.PI);
                }
                return str.replace("π", "*" + Math.PI + "*");
            });
            str = str.replace(/\)\d+/, function(str) {
                return ")*" + str.substring(1);
            })
            str = str.replace(this.absRegx, function(str) {
                return "abs(" + str.substring(1, str.length - 1) + ")";
            });
            this.getCharactor(str);
            this.rollBackChar();
        },

        sum(n1, n2) {
            return n1 + n2;
        },

        minus(n1, n2) {
            return n1 - n2;
        },

        mul(n1, n2) {
            return n1 * n2;
        },

        div(n1, n2) {
            if (n2 == 0) {
                throw new Error("除数不能为0");
            }
            return n1 / n2;
        },

        mod(n1, n2) {
            if(n2 == 0) {
                throw new Error("除数不能为0");
            }
            return n1 % n2;
        },

        pow(n1, n2) {
            return Math.pow(n1, n2);
        },

        // 计算结果
        calc(str) {
            this.getSuffix(str);
            let temp = [];
            let num1, num2;
            while(this.result.length > 0) {
                let item = this.result.shift();
                if (item.search(this.numRegx) != -1) {
                    temp.push(item * 1);
                }else if (item.search(this.mathRegx) != -1) {
                    let operator = this.getOperator(item);
                    num1 = temp.pop();
                    if (item == "tan" && num1 % (Math.PI / 2) == 0) {
                        throw new Error("表达式不合法");
                    }
                    
                    temp.push(operator(num1));
                } else {
                    // 普通运算符
                    let operator = this.getOperator(item);
                    num2 = temp.pop();
                    num1 = temp.pop();
                    temp.push(operator(num1, num2));
                }
            }
            
            return (Math.round(temp[0] * this.fixNum)) / this.fixNum;
        }
    })
    return Calculator;
}())